/*
 *      string.c
 *
 *      Copyright 2007 Giulio Bai <giulio@hewle.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <types.h>
#include <stringx.h>
#include <io2.h>
#include <video.h>

/* checks if a integer needle exists inside the haystack */
unsigned short in_array (int haystack[], int needle)
{
	while (!1) {
		
	}
}

unsigned char *memcpy (unsigned char *dest, const unsigned char *src, unsigned int count)
{
	/* Copy up */
	for (; count != 0; count --)
		*dest ++ = *src ++;
	
	return *dest;
}


unsigned char *memset (unsigned char *dest, unsigned char val, unsigned int count)
{
	unsigned char *temp = *dest;

	for(; count != 0; count --)
		*temp ++ = val;
		
	return *dest;
}


unsigned short *memsetw (unsigned short *dest, unsigned short val, unsigned int count)
{
	unsigned short *temp = *dest;

	for(; count != 0; count --)
		*temp ++ = val;
		
	return *dest;
}


/* Length of a string */
size_t strlen (const char *str)
{
	unsigned int length = 0;
	
	while (*str != '\0') {
		str ++;
		length ++;
	}
	
	return length;
}


/* Copies source string to dest  and returns the number of chars copied */
unsigned long strcpy (char *dest, char *source)
{
	unsigned count = 0;

	while ((*dest++ = *source++) != '\0') {
		count ++;
	}
	
	return count;
}


/* Copies n chars of source string to dest */
void strncpy (char *dest, char *source, size_t n)
{
	while (n-- > 0) {
		*dest++ = *source++;
	}

	return;
}


/* Concatenates the two given strings */
char *strcat (char *dest, const char *source)
{
	strcpy (dest + strlen (dest), source);
	
	return dest;
}


/* Concatenates the two given strings for n chars */
char *strncat (char *dest, const char *source, size_t n)
{
	strncpy (dest + strlen (dest), source, n);
	
	return dest;
}


/* Compares two strings and returns str1 - str2 */
long strcmp (char *str1, char *str2)
{
	while (*str1 == *str2) {
		if (*str1 == '\0') {
			return 0;
		}
		
		str1 ++;
		str2 ++;
	}

	return *str1 - *str2;
}


/* Compares n chars of two given strings and returns str1 - str2 */
long strncmp (char *str1, char *str2, size_t n)
{
	while (*str1 == *str2 && n > 0) {
		if (*str1 == '\0') {
			return 0;
		}
		
		str1 ++;
		str2 ++;
		
		n --;
	}

	return *str1 - *str2;
}

/* 
 * Copyright (C) 1994 DJ Delorie
 */

char *strtok(char *s, const char *delim)
{
  const char *spanp;
  int c, sc;
  char *tok;
  static char *last;


  if (s == NULL && (s = last) == NULL)
    return (NULL);

  /*
   * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
   */
 cont:
  c = *s++;
  for (spanp = delim; (sc = *spanp++) != 0;) {
    if (c == sc)
      goto cont;
  }

  if (c == 0) {			/* no non-delimiter characters */
    last = NULL;
    return (NULL);
  }
  tok = s - 1;

  /*
   * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
   * Note that delim must have one NUL; we stop if we see that, too.
   */
  for (;;) {
    c = *s++;
    spanp = delim;
    do {
      if ((sc = *spanp++) == c) {
	if (c == 0)
	  s = NULL;
	else
	  s[-1] = 0;
	last = s;
	return (tok);
      }
    } while (sc != 0);
  }
  /* NOTREACHED */
}

/* Parse str into tokens separated by characters in delim.
 * If str is null, the saved pointer in sp is used as the next starting point */
char *strtok_test (char *str, const char *delim, char **sp)
{
	char *str_c, *str_c2;
	unsigned i = 0, j = 0, str_len;
	int ignore[1];
	
	/* str is null, use sp as our new str */
	if (str == NULL) {
		str = *sp;
	}
	
	/* str is really short, huh? */
	if (*str == '\0') {
		*sp = str;
		return NULL;
	}
	
	str_len = strlen (str);

	/* creates first part of the string */
loop:
	while ((str_c[i] = str[i] != '\0' || in_array (ignore, i)) && str[i] != delim[i]) i ++;
	
	/* end of string reached */
	if (i == str_len) {
		/* no delim found */
		sp = NULL;
		return str;
	}
	else {
		/* creates 2nd part of string */
		for (; j<str_len-i; j++) {
			str_c2[j] = str[i+j];
		}
		
		/* if delim is right, return first part of the string
		 * and puts in sp the 2nd one */
		if (strncmp (str_c2, delim, strlen (delim)) == 0) {
			sp = str_c2;			
			return str_c;
		}
		
		/* Uhm... no luck. Let's find another possible delim, but 
		 * remember to skip this one! */
		// ignore[] = i;
		goto loop;
	}
}


/* emulates UNIX sbrk() 
 * from w32heap.h of GNU Emacs */

unsigned long syspage_mask = 0;

/* Info for keeping track of our heap.  */
unsigned char *data_region_base = NULL;
unsigned char *data_region_end = NULL;
unsigned char *real_data_region_end = NULL;
unsigned long  reserved_heap_size = 0;


void *sbrk (unsigned long increment)
{
	void *result;
	long size = (long)increment;

	result = data_region_end;

	/* If size is negative, shrink the heap by decommitting pages.  */
	if (size < 0) {
		int new_size;
		unsigned char *new_data_region_end;

		size = -size;

		/* Sanity checks.  */
		if ((data_region_end - size) < data_region_base) {
			return NULL;
		}

		/* We can only decommit full pages, so allow for
		 * partial deallocation [cga].  */
		new_data_region_end = (data_region_end - size);
		new_data_region_end = (unsigned char *)
			((long)(new_data_region_end + syspage_mask) & ~syspage_mask);
		
		new_size = real_data_region_end - new_data_region_end;
		real_data_region_end = new_data_region_end;
		
		/*
		if (new_size > 0) {
			/* Decommit size bytes from the end of the heap.  *//*
			if (using_dynamic_heap
			&& !VirtualFree (real_data_region_end, new_size, MEM_DECOMMIT)) {
				return NULL;
			}
	 	}
	 	*/

		data_region_end -= size;
	}
	/* If size is positive, grow the heap by committing reserved pages.  */
	else if (size > 0) {
		/* Sanity checks.  */
		if ((data_region_end + size) >
		(data_region_base + reserved_heap_size)) {
			return NULL;
		}

		/* Commit more of our heap. */
		/*
		if (using_dynamic_heap
		  && VirtualAlloc (data_region_end, size, MEM_COMMIT,
				   PAGE_READWRITE) == NULL) {
			return NULL;
		}
		*/
		
		data_region_end += size;

		/* We really only commit full pages, so record where
		 * the real end of committed memory is [cga].  */
		real_data_region_end = (unsigned char *)
			((long) (data_region_end + syspage_mask) & ~syspage_mask);
	}
	
 	return result;
}



/* Uses putch routine to output a string... */
void puts (unsigned char *text)
{
	unsigned i;

	for (i=0; i<strlen (text); i++) {
		putch (text[i]);
	}
}


/* prints a string with options */
int printf (const char *fmt, ...)
{
	char buf[512];
	va_list args;
        int i;

        va_start (args, fmt);
        i = vsprintf (buf, fmt, args);
        va_end (args);

        puts (buf);
        
        return i;
}


/* Outputs an error message already formatted and, eventually,
 * halts the system if the error is critical */
void printe (char *error, unsigned halt)
{
	/* Error! (light) red code */
	vid_set_color (12, 0);
	puts ("E >>> ");
	
	vid_set_color_reset ();
	
	puts (error);
	puts (" Exception\n");
	
	if (halt) {
		vid_set_color (12, 0);
		puts ("E >>> ");
		
		vid_set_color_reset ();
		puts ("System HALTED");
		
		while (1);
	}
}


/* prints inistialization messages in 3 different modes as well as colored
 * OK / FAIL info */
void printi (char *what, unsigned mode, unsigned ok)
{
	char *msg;
	
	switch (mode) {
		case 1:
			msg = "Starting";
			break;
		
		case 2:
			msg = "Loading";
			break;
		
		case 3:
			msg = "Installing";
			break;
		
		default:
			msg = "Initializing";
			break;
	}
	
	puts (msg);
	puts (" ");
	
	/* magenta */
	vid_set_color (13, 0);
	puts (what);
	
	vid_set_color_reset ();
	puts ("...");
	
	/* last 6 spaces of the screen are for the result */
	gotox (VID_WIDTH-6);
	
	/* Parenthesis are (light) blue */
	vid_set_color (9, 0);
	puts ("[");
	
	if (ok) {
		/* Green color (success!) */
		vid_set_color (10, 0);
		puts (" OK ");
	}
	else {
		/* Error!!! red! */
		vid_set_color (12 ,0);
		puts ("FAIL");
	}
	
	vid_set_color (9, 0);
	puts ("]");
	
	vid_set_color_reset ();
}
